package com.zhouyu.springframework;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZhouyuApplicationContext {

    //扫描路径
    private Class configClass;
    //bean信息map集合
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    //单例池
    private Map<String, Object> singletonObjects = new HashMap<>();
    //初始化前对象集合
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    //初始化
    public ZhouyuApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 扫描
        scan(configClass);

        //遍历beanDefinitionMap集合，如果是非懒加载的单例bean，就创建bean对象，并加入到单例池中
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton") && !beanDefinition.isLazy()) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * 扫描
     *
     * @param configClass
     */
    private void scan(Class configClass) {
        // 获取要扫描的包路径
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan annotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = annotation.value();

            path = path.replace(".", "/");

            //获取到扫描路径下面的资源
            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());

            //提取所有的文件（类）
            List<File> classFile = new ArrayList<>();
            if (file.isDirectory()) {
                for (File f : file.listFiles()) {
                    if (f.isDirectory()) {
                        for (File f1 : f.listFiles()) {
                            if (!f1.isDirectory()) {
                                classFile.add(f1);
                            }
                        }
                    } else {
                        classFile.add(f);
                    }
                }
            }

            //遍历文件（类）
            for (File cFile : classFile) {
                //截取类名
                String absolutePath = cFile.getAbsolutePath();
                String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                        .replace("\\", ".");
                try {
                    //获取类信息
                    Class<?> clazz = classLoader.loadClass(className);

                    //存在Component注解才走下一步
                    if (clazz.isAnnotationPresent(Component.class)) {
                        //判断对象是否继承了BeanPostProcessor接口，如果是，调用构造方法实例化对象并加入到BeanPostProcessor对象集合中
                        if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getConstructor().newInstance();
                            beanPostProcessorList.add(beanPostProcessor);
                        }

                        //构建类的beanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);
                        beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));
                        //注入类的使用范围，默认单例
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                        } else {
                            beanDefinition.setScope("singleton");
                        }

                        //获取Component注解上面指定的bean名称，为空就生成一个，默认为类名头字母小写
                        String beanName = clazz.getAnnotation(Component.class).value();
                        if (beanName.isEmpty()) {
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }

                        //加入bean信息map集合，key为bean名称，相同的bean名称会覆盖
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建bean对象并返回
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //获取bean类型
        Class clazz = beanDefinition.getType();
        try {
            //创建bean对象
            Object o = clazz.newInstance();

            //遍历bean对象所有的字段，给带有Autowired注解的字段赋值
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object bean = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(o, bean);
                }
            }

            //回调接口，注入自己的bean名称，还有很多的回调方法
            if (o instanceof BeanNameAware) {
                ((BeanNameAware) o).setBeanName(beanName);
            }

            //如果类继承了ApplicationContextAware接口
            if (o instanceof ApplicationContextAware) {
                ((ApplicationContextAware) o).setApplicationContext(this);
            }

            //初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(o,beanName);
            }

            //初始化
            if (o instanceof InitializingBean) {
                ((InitializingBean) o).afterPropertiesSet();
            }

            //初始化后，可以用来aop
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                o = beanPostProcessor.postProcessAfterInitialization(o, beanName);
            }

/*            //如果类上面有Transactional注解，此处要进行aop，创建代理对象，被代理对象赋值给了代理对象的target属性。最后返回的是代理对象，但是执行方法的是被代理对象，是target属性执行的方法
            if (clazz.isAnnotationPresent(Transactional.class)) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(clazz);
                Object target = o;
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("开启事务");
                        Object result = method.invoke(target, objects);
                        System.out.println("提交事务");
                        return result;
                    }
                });
                o = enhancer.create();
            }*/

            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据bean的名称，获取bean对象
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        //通过bean的名称获取到bean的beanDefinition
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        //拿不到说明没有这个名称的bean，报异常
        if (beanDefinition == null) {
            throw new NullPointerException();
        }

        //如果是单例bean，直接从单例池中获取
        if ("singleton".equals(beanDefinition.getScope())) {
            Object result = singletonObjects.get(beanName);

            //单例池中没有，就创建bean对象，并加入到单例池中
            if (result == null) {
                result = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, result);
            }
            return result;
        } else {
            //如果不是单例bean，每次获取都要创建bean对象
            return createBean(beanName, beanDefinition);
        }
    }
}
